OPENHIRE PROTOCOL SPEC v0.1 · DRAFT
← openhire.dev GitHub
schema.org compatible vendor-neutral · donated ref. implementation: openhire-mcp

The machine-readable contract
behind every job posting.

A small, vendor-neutral extension of schema.org/JobPosting for the agent era — plus the four MCP tool contracts that read it. Anyone can implement either half independently.

Why extend, not replace

schema.org/JobPosting was designed in 2011 for search-engine crawlers — it has no concept of freshness verification, ghost-listing risk, or response accountability. OpenHire doesn't replace it; it adds five fields on top, so any existing JobPosting markup keeps working unchanged.

Schema — 5 new fields

Every OpenHire listing is valid JobPosting markup, plus:

verified_at datetime
Last moment this posting was confirmed live on the employer's own site. e.g. 2026-07-09T08:14:00Z
source enum
First-party origin of this data — never a job board. employer_site | ats_public_api
ghost_score float 0–1
Likelihood this listing is not an active hire — relist frequency + staleness. Lower is better. e.g. 0.04
response_sla_days int
Employer's committed response window. Breach auto-delists the posting. e.g. 7
apply_channel url
Always the employer's own application endpoint. Never a platform inbox. e.g. https://boards.greenhouse.io/…
{
"@type": "JobPosting",
"title": "LLM Platform Engineer",
"datePosted": "2026-07-01",
// ↑ standard schema.org fields
"verified_at": "2026-07-09T08:14:00Z",
"source": "ats_public_api",
"ghost_score": 0.04,
"response_sla_days": 7,
"apply_channel": "https://boards.greenhouse.io/shenliu/jobs/1024"
// ↑ the five OpenHire fields
}

MCP tool contracts

Four tools. That's the entire surface area.

search_jobs(filters) → JobPosting[]
Hard-filter the live index. Every result includes verified_at, ghost_score, and apply_channel.
// request
{
"skills": ["rust","k8s","rag"],
"remote": true,
"min_salary": 600000
}
// response
[ {
"title": "LLM Platform Engineer",
"ghost_score": 0.04,
"apply_channel": "https://…"
} ]
watch_intent(fingerprint, filters) → { watch_id }
Registers a standing intent. New matches push to the client — no polling required.
// request
{
"fingerprint": "#a3f9",
"skills": ["rust","k8s"],
"remote": true
}
// response
{
"watch_id": "w_8841",
"status": "active"
}
get_company_info(company_id) → trust signals
Aggregate, anonymous trust signals for one employer — never individual candidate data.
// request
{ "company_id": "shenliu-tech" }
// response
{
"verified": true,
"ghost_score_avg": 0.03,
"response_rate_7d": 0.82
}
apply(job_id, fingerprint, authorized) → delivery receipt
Requires an explicit per-job authorization. Delivers through the employer's own channel — never accepts or stores a résumé file.
// request
{
"job_id": "1024",
"fingerprint": "#a3f9",
"authorized": true
}
// response
{
"delivered_via": "employer_ats",
"receipt_id": "r_5567",
"resume_transmitted": false
}
THREE RULES NO IMPLEMENTATION MAY BREAK
1. A résumé or any PII never transits the server — only an anonymous fingerprint does.
2. Ranking is never a paid parameter — it is only a function of match quality and freshness.
3. Employers pay only for authorized, delivered outcomes — never for exposure.
Status: draft, open for public comment. This spec is designed to be forkable — the goal is adoption, not ownership. Reference implementation: openhire-mcp (MIT).